//
//  SocketManager.swift
//  app_rn_frontend
//
//  Created by 鱼涛 on 2017/12/13.
//  Copyright © 2017年 Facebook. All rights reserved.
//

import Foundation
import SocketIO
import SwiftHash
import SwiftyJSON
import WCDBSwift
import SwiftyUserDefaults

enum ConnectionStatus: Int {
    case NotConnect = 0
    case Connecting = 1
    case ConnectOK = 2
    case ConnectFailed = 3
    case Login = 4
    case LoginOK = 5
    case LoginFailed = 6
    case Sync = 7
    case SyncOK = 8
    case LoseConnection = 9
}


class ConnectionManager {
    static let instance = ConnectionManager()
    
    private var manager: SocketManager?
    
    private var cipher = Cipher(key: sec_key, iv: sec_iv)
    
    var status:ConnectionStatus = .NotConnect{
        didSet {
            NotificationCenter.default.post(name: connectionStatusChange, object: nil, userInfo: ["newStatus": self.status.rawValue])
        }
    }
    
    lazy var handlerQueue = DispatchQueue(label: "com.tst.queue",qos:.background)
    lazy var dbQueue = DispatchQueue(label: "com.tst.db",qos:.background)
    
    private var needInit: Bool = true
    
    private init() {
    }
    
    func initSocket() {
        let server = Defaults[.server] ?? DEFAULT_IM_SERVER_URL_STRING
        self.manager = SocketManager(socketURL: URL(string: "\(server)")!,
                                     config: [.log(true), .compress,
                                              .forceWebsockets(true),
                                              .handleQueue(self.handlerQueue),
                                              .selfSigned(true)])
        self.manager?.handleQueue = self.handlerQueue
        let socket = self.manager?.defaultSocket
        
        socket?.on(clientEvent: .connect) { data, ack in
            self.status = .ConnectOK
            self.login()
        }
        
        socket?.on(clientEvent: .disconnect) { data, ack in
            self.status = .NotConnect
            print("socket disconnected")
        }
        
        socket?.on(clientEvent: .error) { data, ack in
            self.status = .ConnectFailed
            print("socket error")
            print(data[0])
        }
        
        socket?.on(clientEvent: .reconnect) { data, ack in
            print("socket reconnect")
        }
        
        socket?.on(clientEvent: .reconnectAttempt) { data, ack in
            print("socket reconnectAttempt")
        }
        
        socket?.on("newMsg") { data, ack in
            if (data.count > 0) {
                EventProcessorWrapper().process(eventName: "newMessageDidExist", params: data[0] as? Dictionary<String, Any>)
            }
        }
        
        socket?.on("syncFriendRes") { data, ack in
            if (data.count > 0) {
                EventProcessorWrapper().process(eventName: "contactListDidSync", params: data[0] as? Dictionary<String, Any>)
            }
        }
        
        socket?.on("syncGroupRes") { data, ack in
            if data.count > 0 {
                EventProcessorWrapper().process(eventName: "groupListDidSync", params: data[0] as? Dictionary<String, Any>)
            }
        }
        
        socket?.on("syncMsgRes") { data, ack in
            if (data.count > 0) {
                EventProcessorWrapper().process(eventName: "messageDidSync", params: data[0] as? Dictionary<String, Any>)
            }
        }
        
        socket?.on("kickout") { data,ack in
            self.manager?.disconnect()
            self.status = .NotConnect
            print("被踢下线")
            //todo 以后对于断开原因做额外处理
            //            let type = data[0] as? String
            //            if type == "2" {
            //
            //
            //            }
        }
    }
    
    func connect() {
        if needInit {
            needInit = false
            initSocket()
        }
        
        if (self.manager?.status == SocketIOStatus.connected || self.manager?.status == SocketIOStatus.connecting) {
            return
        }
        self.manager?.connect()
    }
    
    func disconnect() {
        if (self.manager?.status == SocketIOStatus.disconnected || self.manager?.status == SocketIOStatus.notConnected) {
            return
        }
        self.manager?.disconnect()
    }
    
    func drop() {
        self.needInit = true
        if (self.manager?.status == SocketIOStatus.disconnected || self.manager?.status == SocketIOStatus.notConnected) {
            return
        }
        self.manager?.disconnect()
    }
    
    @objc func login() {
        let userId:Int = AppStatusManager.instance.userLoginStatus.1 ?? 0
        let data = [
            "userId": userId,
            "version": 1,
        ] as [String: Any]
        
        self.status = .Login
        self.manager?.defaultSocket.emitWithAck("loginReq", data).timingOut(after: 5) { ackData in
            if (ackData.count > 0 && "\(ackData[0])" == SocketAckStatus.noAck.rawValue) {
                let timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(ConnectionManager.login), userInfo: nil, repeats: false)
                timer.fire()
            } else {
                self.status = .LoginOK
                self.syncFriend(userId: userId)
                self.syncGroup(userId: userId)
            }
        }
    }
    
    private func initContactAndGroup(){
        do{
            //            try ContactDao.updateAllDeleted()
            try GroupDao.updateAllDeleted()
            try SessionDao.updateAllGroupDeleted()
        } catch {
            print(error)
        }
    }
    
    func syncFriend(userId: Int) {
        let data = [
            "userId": userId,
            "version": 1,
        ] as [String: Any]
        
        self.manager?.defaultSocket.emit("syncFriendReq", data)
    }
    
    func syncGroup(userId: Int) {
        let data = [
            "userId": userId,
            "version": 1,
        ] as [String: Any]
        self.manager?.defaultSocket.emit("syncGroupReq", data)
    }
    
    func syncMessage(userId: Int, syncKey: Int) {
        let data = [
            "userId": userId,
            "syncKey": syncKey,
            "version": 2,
        ] as [String: Any]
        
        self.manager?.defaultSocket.emit("syncMsgReq", data)
    }
    
    func syncMsgConfirm(userId:Int,syncKey:Int) {
        let data = [
            "userId": userId,
            "syncKey": syncKey,
            "version": 2,
        ] as [String: Any]
        
        self.manager?.defaultSocket.emit("syncMsgConfirm", data)
    }
    
    func sendMessage(message: Dictionary<String, Any>) {
        guard let msg = cipher.encrypt(message["messageContent"] as! String) else {
            return
        }
        
        let data = [
            "msg": msg,
            "mType": message["messageType"]!,
            "from": message["userId"]!,
            "fromName": message["fromName"]!,
            "to": message["sessionServerId"]!,
            "cType": message["chatType"]!,
            "localId": message["localId"]!,
            "at": message["at"]!,
            "version": 1,
        ] as [String: Any]
        
        self.manager?.defaultSocket.emit("sndMsg", data)
    }
    
    func sendMessage(message:ChatMessage,result: @escaping (_ message:ChatMessage) -> Void) {
        guard let msg = cipher.encrypt(message.text ?? "") else {
            return
        }
        let data = [
            "msg": msg,
            "mType": message.messageType.rawValue,
            "from": message.from,
            "fromName": message.fromName,
            "to": message.sessionId,
            "cType": message.chatType.rawValue,
            "localId": message.localId ?? 0,
            "at": [],
            "version": 1,
        ] as [String: Any]
        
        self.manager?.defaultSocket.emitWithAck("sndMsg", data).timingOut(after: 30) { ackData in
            if (ackData.count > 0 && "\(ackData[0])" == SocketAckStatus.noAck.rawValue) {
                message.status = .error
            } else {
                message.status = .success
            }
            result(message)
        }
    }
}
